/*
 * @Author: huanggang
 * @Date: 2021-08-10 17:09:15
 * @LastEditTime: 2021-11-25 22:03:33
 * @LastEditors: huanggang
 * @Description: In User Settings Edit
 * @FilePath: \undefinedc:\Users\hg\Desktop\util_udp.c
 */

#include "util_udp.h"

/**
 * @description: 加入多播组
 * @author: huanggang
 * @Date: 2021-11-24 23:38:32
 * @param {UDP_INFO_T} *udp udp上下文
 * @param {char} *multi_ip 多播组IP
 * @return {*} 成功返回0,失败返回-1
 */
int udp_enable_multicast(UDP_INFO_T *udp, char *multi_ip)
{
    struct ip_mreq mreq;
    struct in_addr if_addr;
    struct ifreq ifr;
    int loop = 0;

    if (udp->fd <= 0)
    {
        fprintf(stderr, "%s socket not init\n", __func__);
        return -1;
    }

    memset(&ifr, 0, sizeof(struct ifreq));
    if (udp->intf != NULL)
    {
        strcpy((char *)ifr.ifr_name, (const char *)udp->intf);
    }
    if (ioctl(udp->fd, SIOCGIFADDR, &ifr) < 0)
    {
        perror("ioctl SIOCGIFADDR failed");
        close(udp->fd);
        return -1;
    }
    memcpy(&if_addr, &(((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr), sizeof(struct in_addr));

    memset(&mreq, 0, sizeof(mreq));
    mreq.imr_multiaddr.s_addr = inet_addr(multi_ip);
    mreq.imr_interface.s_addr = if_addr.s_addr;
    if (setsockopt(udp->fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
    {
        perror("setsockopt IP_ADD_MEMBERSHIP");
        return -1;
    }

    // 禁止收到自身发送的数据
    if (setsockopt(udp->fd, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop)) == -1)
    {
        printf("IP_MULTICAST_LOOP set fail!\n");
    }
    return 0;
}

/**
 * @description: 退出多播组
 * @author: huanggang
 * @Date: 2021-11-24 23:40:56
 * @param {UDP_INFO_T} *udp udp上下文
 * @param {char} *multi_ip 多播组IP
 * @return {*} 成功返回0,失败返回-1
 */
int udp_disable_multicast(UDP_INFO_T *udp, char *multi_ip)
{
    struct ip_mreq mreq;
    memset(&mreq, 0, sizeof(mreq));
    mreq.imr_multiaddr.s_addr = inet_addr(multi_ip);
    mreq.imr_interface.s_addr = htonl(INADDR_ANY);

    if (udp->fd <= 0)
    {
        fprintf(stderr, "%s socket not init\n", __func__);
        return -1;
    }

    if (setsockopt(udp->fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
    {
        perror("setsockopt IP_ADD_MEMBERSHIP");
        return -1;
    }
    return 0;
}

int udp_enable_broadcast(UDP_INFO_T *udp)
{
    int ret, enable = 1;

    if (udp->fd <= 0)
    {
        fprintf(stderr, "%s socket not init\n", __func__);
        return -1;
    }

    ret = setsockopt(udp->fd, SOL_SOCKET, SO_BROADCAST, (char *)&enable, sizeof(enable));
    if (ret < 0)
    {
        fprintf(stderr, "setsockopt SO_BROADCAST failed\n");
        return -1;
    }
    return 0;
}

/**
 * @description: UDP初始化,适用于单播,多播和广播
 * @author: huanggang
 * @Date: 2021-11-24 23:41:39
 * @param {UDP_INFO_T} *udp udp上下文
 * @param {int} local_port 本地端口,客户端时可以省略,服务器必须指定
 * @param {char} *dst_ip 目的地址IP,服务器可以省略,客户端如果通信方固定可以写入
 * @param {int} dst_port 目的地址端口,服务器可以省略,客户端如果通信方固定可以写入
 * @return {*}
 */
int udp_init(UDP_INFO_T *udp, char *interface, int local_port)
{
    int ret;
    struct sockaddr_in local_addr;
    int fd;

    fd = socket(AF_INET, SOCK_DGRAM, 0);
    if (fd == -1)
    {
        perror("socket failed!\n");
        return -1;
    }

    udp->fd = fd;

    if (interface)
    {
        udp->intf = interface;
    }

    /* 绑定本地套接字 */
    if (local_port > 0)
    {
        local_addr.sin_family = AF_INET;
        local_addr.sin_port = htons(local_port);
        local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
        ret = bind(fd, (struct sockaddr *)&local_addr, sizeof(local_addr));
        if (ret < 0)
        {
            fprintf(stderr, "%s bind fail\n", __func__);
            close(fd);
            return -1;
        }
    }
    return 0;
}

int udp_set_dst(UDP_INFO_T *udp, char *dst_ip, int dst_port)
{
    struct sockaddr_in dst = {0};

    if (udp->fd <= 0)
    {
        fprintf(stderr, "%s socket not init\n", __func__);
        return -1;
    }

    if (dst_ip != NULL)
    {
        dst.sin_family = AF_INET;
        dst.sin_port = htons(dst_port);
        dst.sin_addr.s_addr = inet_addr(dst_ip);
        udp->dst = dst;
    }

    return 0;
}

/**
 * @description: udp发送,对于客户端目的地址就是初始化指定,对于服务器目的地址是刚接受的客户端的地址,建议客户端使用
 * @author: huanggang
 * @Date: 2021-11-24 23:58:31
 * @param {UDP_INFO_T} *udp udp上下文
 * @param {void} *buf 要发送的数据起始地址
 * @param {int} len 要发送的数据长度
 * @return {*} 失败返回-1,成功返回发送的数据字节长度
 */
int udp_send2dst(UDP_INFO_T *udp, void *buf, int len)
{
    if (udp->fd <= 0)
    {
        fprintf(stderr, "%s socket not init\n", __func__);
        return -1;
    }
    int ret = sendto(udp->fd, buf, len, 0, (struct sockaddr *)(&udp->dst), sizeof(struct sockaddr));
    return ret;
}

int udp_send2peer(UDP_INFO_T *udp, void *buf, int len)
{
    if (udp->fd <= 0)
    {
        fprintf(stderr, "%s socket not init\n", __func__);
        return -1;
    }
    int ret = sendto(udp->fd, buf, len, 0, (struct sockaddr *)(&udp->peer), sizeof(struct sockaddr));
    return ret;
}

/**
 * @description:
 * @author: huanggang
 * @Date: 2021-11-24 23:58:41
 * @param {UDP_INFO_T} *udp
 * @param {void} *buf
 * @param {int} len
 * @param {char} *dst_ip
 * @param {int} dst_port
 * @return {*}
 */
int udp_sendto(UDP_INFO_T *udp, void *buf, int len, char *dst_ip, int dst_port)
{
    struct sockaddr_in peer = {0};
    peer.sin_family = AF_INET;
    peer.sin_port = htons(dst_port);
    peer.sin_addr.s_addr = inet_addr(dst_ip);
    if (udp->fd <= 0)
    {
        fprintf(stderr, "%s socket not init\n", __func__);
        return -1;
    }
    int ret = sendto(udp->fd, buf, len, 0, (struct sockaddr *)(&peer), sizeof(struct sockaddr));
    return ret;
}

/**
 * @description:
 * @author: huanggang
 * @Date: 2021-11-24 23:58:46
 * @param {UDP_INFO_T} *udp
 * @param {void} *buff
 * @param {int} length
 * @param {int} ms
 * @return {*}
 */
int udp_recv(UDP_INFO_T *udp, void *buff, int length, int ms)
{
    int ret;
    struct timeval tv, *tmt;
    socklen_t len = sizeof(udp->peer);
    fd_set rset;
    FD_ZERO(&rset);
    FD_SET(udp->fd, &rset);

    if (udp->fd <= 0)
    {
        fprintf(stderr, "%s socket not init\n", __func__);
        return -1;
    }

    if (ms < 0)
    {
        tmt = NULL;
    }
    else
    {
        tv.tv_sec = ms / 1000;
        tv.tv_usec = (ms % 1000) * 1000;
        tmt = &tv;
    }

    ret = select(udp->fd + 1, &rset, NULL, NULL, tmt);
    if (ret == 0)
    {
        return 0;
    }
    else if (ret < 0)
    {
        fprintf(stderr, "%s select\n", __func__);
        return -1;
    }
    else
    {
        ret = recvfrom(udp->fd, buff, length, 0, (struct sockaddr *)&udp->peer, &len);
        return ret;
    }
}
